package com.lanchetech.user.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.bean.dto.*;
import com.lanchetech.bean.request.PageReq;
import com.lanchetech.bean.response.BasePageResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.*;
import com.lanchetech.common.enums.*;
import com.lanchetech.common.util.SensitiveInfoUtil;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.DaoService;
import com.lanchetech.user.service.LotteryService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


@Service
public class LotteryServiceImpl implements LotteryService {

    @Autowired
    LotteryMapper lotteryMapper;

    @Autowired
    LotteryRecordMapper lotteryRecordMapper;

    @Autowired
    CoinProductMapper coinProductMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    CoinOrderMapper coinOrderMapper;

    @Autowired
    CoinOrderAddressMapper coinOrderAddressMapper;

    @Autowired
    UserAddressMapper userAddressMapper;

    @Autowired
    CoinFlowMapper coinFlowMapper;

    @Autowired
    BalanceFlowMapper balanceFlowMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    LotteryOpportunityMapper lotteryOpportunityMapper;

    @Autowired
    SpuMapper spuMapper;

    @Autowired
    private LotteryOrderMapper lotteryOrderMapper;

    @Autowired
    private UserAmtLogMapper userAmtLogMapper;

    @Autowired
    private CardConsumptionUserMapper cardConsumptionUserMapper;

    @Autowired
    private CardFlowMapper cardFlowMapper;

    @Autowired
    private ShopMapper shopMapper;

    @Override
    public ResultData<LotteryInfoVO> getLotteryByType(User user, Byte type) {
        LotteryInfoVO vo = new LotteryInfoVO();
        Lottery lottery = lotteryMapper.findOneByStatusAndDeletedAndType(ToggleEnum.ON.getStatus(), DeletedEnum.DEFAULT.getStatus(), type, new Date());
        if (lottery == null) {
            return new ResultData<>(vo);
        }
        vo.setLottery(lottery);
        List<LotterySettingDTO> settingList = JSON.parseArray(lottery.getSetting(), LotterySettingDTO.class);
        List<Long> ids = new ArrayList<>();
        settingList.stream().forEach(item -> {
            if (PrizeTypeEnum.PRODUCT.getType().equals(item.getPrizeType())) {
                ids.add(item.getCoinProductId());
            }
        });

        if (!CollectionUtils.isEmpty(ids)) {
            vo.setCoinProductList(coinProductMapper.findAllByIds(ids));
        }

        if (user != null) {
            // 统计此用户的参数次数
            Long count = lotteryRecordMapper.countByUserIdAndLotteryId(user.getId(), lottery.getId());
            vo.setCount(count);
            vo.setLotteryOpportunityCount(lotteryOpportunityMapper.countByStatusAndUserId(LotteryOpportunityStatusEnum.NOT_USED.getStatus(), user.getId()));
        }

        return new ResultData<>(vo);
    }

    @Override
    public ResultData<BasePageResp<LotteryRecordVO>> getLotteryRecordPage(PageReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (req.getRelateId() == null) {
            req.setUserId(user.getId());
        }
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<LotteryRecord> list = lotteryRecordMapper.findAllPage(req);
        List<LotteryRecordVO> result = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, Lottery> map = daoService.getLotteryMap(list.stream().map(LotteryRecord::getLotteryId).collect(Collectors.toList()));
            Map<Long, User> userMap = req.getRelateId() != null ? daoService.getUserMap(list.stream().map(LotteryRecord::getUserId).collect(Collectors.toList())) : new HashMap<>();

            list.stream().forEach(item -> {
                LotteryRecordVO vo = new LotteryRecordVO();
                BeanUtils.copyProperties(item, vo);
                vo.setLottery(map.get(item.getLotteryId()));

                if (req.getRelateId() != null) {
                    vo.setAvatar(userMap.get(item.getUserId()).getAvatar());
                    vo.setNickname(SensitiveInfoUtil.commonDisplay(userMap.get(item.getUserId()).getNickname()));
                }

                result.add(vo);
            });
        }
        return PageHelpUtil.buildPage(result, page);
    }

    /**
     * 计算抽奖结果，得到中奖的对象，获将对象的排序后的中奖索引，0开始，数字越大越靠后，相同则顺序
     *
     * @return
     */
    private static LotterySettingDTO computedProbability(List<LotterySettingDTO> settingList) {
        // 先根据sorts排序
        settingList = settingList.stream().sorted(Comparator.comparing(LotterySettingDTO::getSorts)).collect(Collectors.toList());

        BigDecimal current = BigDecimal.ZERO;
        int result = -1;
        for (int i = 0; i < settingList.size(); i++) {
            settingList.get(i).setStartNum(current);
            if (settingList.get(i).getProbability().compareTo(BigDecimal.ZERO) > 0) {
                current = current.add(settingList.get(i).getProbability().multiply(new BigDecimal(100)));
                settingList.get(i).setEndNum(current);
            } else {
                settingList.get(i).setEndNum(current);
            }
            if (PrizeTypeEnum.THANK.getType().equals(settingList.get(i).getPrizeType())) {
                result = i;
            }
        }

        Random random = new Random();
        BigDecimal lotteryNum = new BigDecimal(random.nextInt(10000));
        for (int i = 0; i < settingList.size(); i++) {
            if (settingList.get(i).getStartNum().compareTo(lotteryNum) < 0 && settingList.get(i).getEndNum().compareTo(lotteryNum) >= 0) {
                result = i;
                break;
            }
        }
        return settingList.get(result);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultData createLotteryRecord(LotteryDto req) {
        ResultData resultData = new ResultData();
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Lottery lottery = lotteryMapper.selectByPrimaryKey(req.getLotteryId());
        Date now = new Date();
        // 判断抽奖活动是否正在进行
        if (ToggleEnum.OFF.getStatus().equals(lottery.getStatus()) || DeletedEnum.DELETED.getStatus().equals(lottery.getDeleted())) {
            resultData.setResultCode(ResultCode.LOTTERY_FINISHED);
            return resultData;
        }
        if (now.before(lottery.getStartTime()) || now.after(lottery.getEndTime())) {
            resultData.setResultCode(ResultCode.LOTTERY_FINISHED);
            return resultData;
        }

        //判断用户抽奖机会是否足够
        LotteryOpportunity lotteryOpportunity = lotteryOpportunityMapper.findOneByStatusAndUserId(LotteryOpportunityStatusEnum.NOT_USED.getStatus(), user.getId());
        if (lotteryOpportunity == null) {
            resultData.setResultCode(ResultCode.LOTTERY_OPPORTUNITY_NOT_ENOUGH);
            return resultData;
        }

        //扣除用户抽奖机会
        lotteryOpportunity.setStatus(LotteryOpportunityStatusEnum.USED.getStatus());
        lotteryOpportunity.setUsedTime(new Date());
        lotteryOpportunity.setUpdatedAt(new Date());
        lotteryOpportunityMapper.updateByPrimaryKeySelective(lotteryOpportunity);


        // 随机抽奖
        List<LotterySettingDTO> settingList = JSON.parseArray(lottery.getSetting(), LotterySettingDTO.class);
        LotterySettingDTO prize = computedProbability(settingList);

        // 创建参与记录
        LotteryRecord lotteryRecord = LotteryRecord.builder()
                .userId(user.getId())
                .lotteryId(lottery.getId())
                .status(PrizeTypeEnum.THANK.getType().equals(prize.getPrizeType()) ? (byte) 0 : (byte) 1)
                .addressStatus(ToggleEnum.OFF.getStatus())
                .type(lottery.getType())
                .createdAt(new Date())
                .updatedAt(new Date())
                .name(lottery.getName())
                .prizeAmount(prize.getPrizeAmount())
                .prizeType(prize.getPrizeType())
                .prizeName(prize.getPrizeName())
                .prizeImage(prize.getPrizeImage())
                .indexNumber(prize.getIndex())
                .build();
        lotteryRecordMapper.insert(lotteryRecord);

        lotteryMapper.addOneJoinCountById(lottery.getId());

        CreateLotteryRecordVo createLotteryRecordVo = new CreateLotteryRecordVo();
        createLotteryRecordVo.setWhetherPrize(true);
        if (PrizeTypeEnum.PRODUCT.getType().equals(prize.getPrizeType())) {
            if (lottery.getCoinAmount().compareTo(prize.getPrizeAmount()) == -1) {
                resultData.setResultCode(ResultCode.COIN_INVENTORY_NOT);
                return resultData;
            }
            CardConsumptionUserVo cardConsumptionUser = cardConsumptionUserMapper.getCardConsumptionUser(user.getId());
            //变动后金额
            BigDecimal costFigure = cardConsumptionUser.getAmt().add(prize.getPrizeAmount());
            UserAmtLog userAmtLog = new UserAmtLog();
            userAmtLog.setUserId(user.getId());
            userAmtLog.setAmt(prize.getPrizeAmount());//变动额度
            userAmtLog.setBeforeAmt(cardConsumptionUser.getAmt());//变动前金额
            userAmtLog.setAfterAmt(costFigure);//变动后金额
            userAmtLog.setCreated(new Date());
            userAmtLog.setRemark("抽奖赠送消费金:+" + prize.getPrizeAmount());
            userAmtLog.setType((byte) 1);
            userAmtLogMapper.insert(userAmtLog);
            //修改消费金账户
            cardConsumptionUser.setAmt(costFigure);
            cardConsumptionUserMapper.updateConsumptionUser(cardConsumptionUser);
            //修改消费金库存
            lotteryMapper.updateCoinInventory(lottery.getId(), prize.getPrizeAmount());
        } else if (PrizeTypeEnum.COIN.getType().equals(prize.getPrizeType())) {
            //嗨币
            if (lottery.getPointAmount().compareTo(prize.getPrizeAmount()) == -1) {
                resultData.setResultCode(ResultCode.POINT_INVENTORY_NOT);
                return resultData;
            }
            BigDecimal cardPoint = userMapper.getCoin(user.getId());
            BigDecimal newCardPoint = cardPoint.add(prize.getPrizeAmount());
            //添加流水记录
            CardFlow cardFlow = new CardFlow();
            cardFlow.setUserId(user.getId());
            cardFlow.setAmount(prize.getPrizeAmount());
            cardFlow.setTradeType(CardTradeTypeEnum.NEW_REGISTRATION.getTradeType());
            cardFlow.setType(CardTradeTypeEnum.NEW_REGISTRATION.getType());
            cardFlow.setCardPoint(newCardPoint);
            cardFlow.setCreatedAt(new Date());
            cardFlow.setRemark("抽奖赠送嗨币:+" + prize.getPrizeAmount());
            cardFlowMapper.insert(cardFlow);
            //修改账户嗨币
            userMapper.updateCardPoint(newCardPoint, user.getId());
            //修改嗨币库存
            lotteryMapper.updatePointInventory(lottery.getId(), prize.getPrizeAmount());
        } else if (PrizeTypeEnum.POINTS.getType().equals(prize.getPrizeType())) {
            Spu spu = spuMapper.selectByPrimaryKey(prize.getPrizeId());
            Shop shop = shopMapper.selectByPrimaryKey(spu.getTenantId());
            //创建spu商品订单
            LotteryOrder lotteryOrder = new LotteryOrder();
            lotteryOrder.setActivityId(lottery.getId());
            lotteryOrder.setLotteryRecordId(lotteryRecord.getId());
            lotteryOrder.setUserId(user.getId());
            lotteryOrder.setSpuId(prize.getPrizeId());
            lotteryOrder.setPrizeImg(spu.getMainImage());
            lotteryOrder.setPrizeName(spu.getName());
            lotteryOrder.setType(LotteryOrderTypeEnum.NEW_PEOPLE_ACTIVITY.getType());
            lotteryOrder.setStatus(LotteryOrderStatusEnum.NO_RECEIVE.getType());
            lotteryOrder.setTenantId(spu.getTenantId());
            lotteryOrder.setCreatedAt(new Date());
            lotteryOrderMapper.insert(lotteryOrder);
            createLotteryRecordVo.setSpuId(spu.getId());
            createLotteryRecordVo.setShopId(spu.getTenantId());
            createLotteryRecordVo.setShopAvatar(shop.getAvatar());
        } else {
            createLotteryRecordVo.setWhetherPrize(false);
        }
        createLotteryRecordVo.setPrizeImg(prize.getPrizeImage());
        createLotteryRecordVo.setPrizeName(prize.getPrizeName());
        createLotteryRecordVo.setAvatar(user.getAvatar());
        return new ResultData<>(createLotteryRecordVo);
    }

    @Override
    public ResultData<Long> createCoinOrderWithAddress(Long lotteryRecordId, Long addressId) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        LotteryRecord lotteryRecord = lotteryRecordMapper.selectByPrimaryKey(lotteryRecordId);
        if (PrizeTypeEnum.PRODUCT.getType().equals(lotteryRecord.getPrizeType())
                && ToggleEnum.ON.getStatus().equals(lotteryRecord.getStatus())
                && ToggleEnum.OFF.getStatus().equals(lotteryRecord.getAddressStatus())) {

            CoinProduct coinProduct = coinProductMapper.selectByPrimaryKey(lotteryRecord.getCoinProductId());

            // 创建订单
            CoinOrder coinOrder = CoinOrder.builder()
                    .coinProductId(lotteryRecord.getCoinProductId())
                    .userId(user.getId())
                    .spuId(coinProduct.getSpuId())
                    .count(1)
                    .totalCostCoin(lotteryRecord.getCostCoin())
                    .logisticsStatus(LogisticsStatusEnum.UNDO.getStatus())
                    .type(lotteryRecord.getType())
                    .userAddressId(addressId)
                    .lotteryStatus(ToggleEnum.ON.getStatus())
                    .createdAt(new Date())
                    .updatedAt(new Date())
                    .build();
            coinOrderMapper.insert(coinOrder);

            UserAddress userAddress = userAddressMapper.selectByPrimaryKey(addressId);
            CoinOrderAddress coinOrderAddress = CoinOrderAddress.builder().build();
            BeanUtils.copyProperties(userAddress, coinOrderAddress);
            coinOrderAddress.setId(null);
            coinOrderAddress.setCoinOrderId(coinOrder.getId());
            coinOrderAddress.setUserAddressId(userAddress.getId());
            coinOrderAddressMapper.insert(coinOrderAddress);

            // 更新收货地址状态
            lotteryRecord.setAddressStatus(ToggleEnum.ON.getStatus());
            lotteryRecord.setCoinOrderId(coinOrder.getId());
            lotteryRecord.setUpdatedAt(new Date());
            lotteryRecordMapper.updateByPrimaryKeySelective(lotteryRecord);

            return new ResultData<>(coinOrder.getId());
        } else {
            ResultData resultData = new ResultData();
            resultData.setResultCode(ResultCode.FAIL);
            return resultData;
        }
    }

    /**
     * 奖品列表
     *
     * @return
     */
    public ResultData lotteryInfo() {
        Lottery lottery = lotteryMapper.findOneByStatusAndDeletedAndType(ToggleEnum.ON.getStatus(), DeletedEnum.DEFAULT.getStatus(), CoinProductTypeEnum.NINE_LOTTERY.getType(), new Date());
        if (null == lottery) {
            ResultData resultData = new ResultData();
            resultData.setResultCode(ResultCode.LOTTERY_NOT);
            return resultData;
        }

        LotteryInfoRestVo lotteryInfoRestVo = new LotteryInfoRestVo();
        List<LotterySettingDTO> settingList = JSON.parseArray(lottery.getSetting(), LotterySettingDTO.class);
        for (LotterySettingDTO lotterySettingDTO : settingList) {
            if (PrizeTypeEnum.POINTS.getType().equals(lotterySettingDTO.getPrizeType())) {
                String shopImg = spuMapper.getShopImg(lotterySettingDTO.getPrizeId());
                lotterySettingDTO.setShopImgs(shopImg);
            }
        }
        lotteryInfoRestVo.setLotterySettingDTO(settingList);
        lotteryInfoRestVo.setDescription(lottery.getDescription());
        lotteryInfoRestVo.setLotteryId(lottery.getId());
        return new ResultData(lotteryInfoRestVo);
    }

    /**
     * 中奖人名单
     *
     * @param lotteryWinnersDto
     * @return
     */
    @Override
    public ResultData lotteryWinners(LotteryWinnersDto lotteryWinnersDto) {
        Page page = PageHelper.startPage(lotteryWinnersDto.getPageNo(), lotteryWinnersDto.getPageSize());
        List<LotteryWinnersVo> winnersVos = lotteryRecordMapper.lotteryWinners(lotteryWinnersDto);
        return PageHelpUtil.buildPage(winnersVos, page);
    }

    /**
     * 订单详情
     *
     * @param lotteryOrderDto
     * @return
     */
    @Override
    public ResultData lotteryOrder(LotteryOrderDto lotteryOrderDto) {
        LotteryOrderVo lotteryOrderVo = lotteryOrderMapper.lotteryOrder(lotteryOrderDto);
        return new ResultData(lotteryOrderVo);
    }

    /**
     * 中奖记录
     *
     * @param lotteryNewRecordDto
     * @return
     */
    @Override
    public ResultData lotteryNewRecord(LotteryNewRecordDto lotteryNewRecordDto) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        lotteryNewRecordDto.setUserId(user.getId());
        Page page = PageHelper.startPage(lotteryNewRecordDto.getPageNo(), lotteryNewRecordDto.getPageSize());
        List<LotteryNewRecordVo> lotteryNewRecordVos = lotteryOrderMapper.lotteryNewRecord(lotteryNewRecordDto);
        return PageHelpUtil.buildPage(lotteryNewRecordVos, page);
    }

    /**
     * 新增收货地址
     *
     * @param addDeliveryAddressDto
     * @return
     */
    @Override
    public ResultData addDeliveryAddress(AddDeliveryAddressDto addDeliveryAddressDto) {
        Long lotteryRecordId = lotteryRecordMapper.queryRecordId(addDeliveryAddressDto.getLotteryOrderId());
        //修改订单收货地址状态
        lotteryOrderMapper.updateAddress(addDeliveryAddressDto.getLotteryOrderId(), addDeliveryAddressDto.getAddressId());
        //修改订单中奖记录
        lotteryRecordMapper.updateAddressStatus(lotteryRecordId);
        return new ResultData(ResultCode.SUCCESS);
    }

    @Override
    public ResultData shopPrize(ShopPrizeDto shopPrizeDto) {
        ShopPrizeVo shopPrizeVo = spuMapper.shopPrize(shopPrizeDto.getSpuId());
        return new ResultData(shopPrizeVo);
    }


}